from django.shortcuts import render
from rest_framework import status
from rest_framework.response import Response
from rest_framework.views import APIView
from django_redis import get_redis_connection
import base64, pickle
from goods.models import SKU


from .serializers import CartSerializer, CartSKUSerializer, CartDeleteSeriazlier, CartSelectedAllSerializer
from .utils import str_to_dict, dict_to_str
# Create your views here.


class CartView(APIView):
    """
    购物车视图
    """
    def perform_authentication(self, request):
        """
        重写⽗类的⽤户验证⽅法，不在进⼊视图前就检查JWT
        保证⽤户未登录也可以进⼊下⾯的请求⽅法
        :param request:
        :return:
        """
        pass




    def post(self, request):
        """
        添加购物车
        :param request:
        :return:
        """
        # 指定序列化器
        serializer = CartSerializer(data=request.data)
        # 验证提交的数据
        serializer.is_valid(raise_exception=True)
        # 获取提交的数据
        sku_id = serializer.data.get("sku_id")
        count = serializer.data.get("count")
        selected = serializer.data.get("selected")
        # 获取请求中的用户.判断是否登录,如果第一次请求,user中是None,
        try:
            user = request.user
        except Exception:
            user = None
        # 验证登录状态
        if user is not None and user.is_authenticated:
            # 登录状态
            # 创建redis的连接
            con_redis = get_redis_connection("carts")
            pl = con_redis.pipeline()
            # HINCRBY 创建一个字典,
            # 此方法如果要添加的key在原哈希中不存就是新增,
            # 如果key已经存在, 就后面的value和原有value相加
            pl.hincrby("cart_%s" % user.id, sku_id, count)

            if selected:
                pl.sadd("selected_%s" % user.id, sku_id)
            pl.execute()
            return Response(serializer.data, status=status.HTTP_201_CREATED)


        else:
            # 没登录状态
            # 用户未登录，在cookie中保存
            # {
            #     1001: { "count": 10, "selected": true},
            #     ...
            # }
            # 使用pickle序列化购物车数据，pickle操作的是bytes类型
            cookie_str = request.COOKIES.get("cart")
            # 判断购物车是否存在
            if cookie_str:
                cookie_str_bytes = cookie_str.encode()
                cookie_dict_bytes = base64.b64decode(cookie_str_bytes)
                cookie_dict = pickle.loads(cookie_dict_bytes)
                if sku_id in cookie_dict:
                    origin_count = cookie_dict[sku_id]["count"]
                    count += origin_count

            else:
                cookie_dict={}

            # 返回包装的字典
            cookie_dict[sku_id] = {
                'count': count,
                'selected': selected
            }

            cookie_dict_bytes = pickle.dumps(cookie_dict)
            cookie_str_bytes = base64.b64encode(cookie_dict_bytes)
            cookie_str = cookie_str_bytes.decode()

            resp = Response(serializer.data, status=status.HTTP_201_CREATED)
            resp.set_cookie("cart",cookie_str)
            return resp


    def get(self, request):
        """
        查询购物车
        :param request:
        :return:
        """
        try:
            user = request.user
        except:
            user = None
        # 是否登录
        if user is not None and user.is_authenticated:
            # 创建redis连接
            redis_con = get_redis_connection("carts")
            # 获取当前用户的cart
            redis_cart = redis_con.hgetall("cart_%s" % user.id)
            # 获取当前用户cart中选中的sku_id
            selected_cart = redis_con.smembers("selected_%s" % user.id)
            # 构造一个跟cookie中格式一样的字典
            card_dict = {}
            for sku_id, count in redis_cart.items():
                card_dict[int(sku_id)] = {
                    "count": int(count),
                    "selected": sku_id in selected_cart
                }
        else:
            cookie_str = request.COOKIES.get("cart")
            if cookie_str:
                #　转换为字典格式
                card_dict = str_to_dict(cookie_str)
            else:
                card_dict = {}

        cart_list =[]
        # 遍历当前用户购物车中所有的商品（获取商品的ｉｄ）
        for sku_id in card_dict:
            # 根据ｉｄ获取商品模型对象
            sku = SKU.objects.get(id=sku_id)
            # 给模型对象添加２个字典，（给类添加属性,如果没有添加，如果有则为修改）
            sku.count = card_dict[sku_id].get("count")
            sku.selected = card_dict[sku_id].get("selected")
            cart_list.append(sku)
        # 序列化
        serializer = CartSKUSerializer(instance=cart_list, many=True)

        return Response(serializer.data)


    def put(self, request):
        """
        修改购物车
        :param request:
        :return:
        """
        # 获取请求数据
        serializer = CartSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        # 获取到验证后的请求数据
        sku_id = serializer.validated_data.get("sku_id")
        count = serializer.validated_data.get("count")
        selected = serializer.validated_data.get("selected")

        try:
            user = request.user
        except:
            user = None

        # 登录状态
        if user is not None and user.is_authenticated:
            # 创建ｒｅｄｉｓ连接
            redis_conn = get_redis_connection("carts")
            # 创建管道
            pl = redis_conn.pipeline()
            # 修改
            pl.hset("cart_%s" % user.id, sku_id, count)

            if selected:
                pl.sadd("selected_%s" % user.id, sku_id)
            else:
                pl.srem("selected_%s" % user.id, sku_id)

            pl.execute()

            return Response(serializer.data)
        # 未登录状态
        else:
            #　获取所有ｃｏｏｋｉｅ
            cookie_str = request.COOKIES.get("cart")
            if cookie_str:
                card_dict = str_to_dict(cookie_str)
            else:
                card_dict = {}

            card_dict[sku_id] = {
                "count": count,
                "selected": selected
            }

            cookie_str = dict_to_str(card_dict)
            resp = Response(serializer.data)
            resp.set_cookie("cart",cookie_str)
            return resp

    def delete(self, request):
        """删除购物车"""
        serializer = CartDeleteSeriazlier(data=request.data)
        serializer.is_valid(raise_exception=True)

        sku_id = serializer.validated_data.get("sku_id")


        try:
            user = request.user
        except:
            user = None

        resp = Response(status=status.HTTP_204_NO_CONTENT)
            # 登录状态下
        if user is not None and user.is_authenticated:
            redis_conn = get_redis_connection("carts")
            pl = redis_conn.pipeline()
            pl.hdel("cart_%s" % user.id, sku_id)
            pl.srem("selected_%s" % user.id, sku_id)
            pl.execute()
        else:
            # 非登录状态下
            cookie_str = request.COOKIES.get("cart")
            if cookie_str:
                cart_dict = str_to_dict(cookie_str)
            else:
                cart_dict ={}
            # 删除当前购物车中选中的项目（字典中对应的ＫＥＹ）
            if sku_id in cart_dict.keys():
                del cart_dict[sku_id]

            if len(cart_dict.keys()):
                cookie_str = dict_to_str(cart_dict)
                resp.set_cookie("cart", cookie_str)
            else:
                resp.delete_cookie("cart")

        return resp




class CartSelectedAllView(APIView):
    """
    购物车全选视图
    """
    def perform_authentication(self, request):
        """默认视图在进行请求分发时就会进行认证
        在视图中重写此方法,如果内部直接pass,表示在请求分发时,先不要认证,让请求可以正常访问
        目的:延后它的认证,为了让未登录用户也能先访问我的视图
        将来自己去写认证,
        """
        pass
    def put(self, request):
        """
        修改ｓｅｌｅｃｔｅｄ属性
        :param request:
        :return:
        """
        serializer = CartSelectedAllSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)

        selected = serializer.validated_data.get("selected")

        try:
            user = request.user
        except:
            user = None
        # 登录状态
        if user is not None and user.is_authenticated:
            # 连接ｒｅｄｉｓ
            redis_con = get_redis_connection("carts")
            # 获取当前用户购物车中的所有商品
            redis_cart_dict = redis_con.hgetall("cart_%s" % user.id)
            # 把所有商品（ｄｉｃｔ）中的ｋｅｙ（ｓｋｕ＿ｉｄ）取出来
            sku_id_keys = redis_cart_dict.keys()
            # 如果请求的selected为true 就把所有keys添加到保持keys的redis中,如果是false就移除
            if selected:
                redis_con.sadd("selected_%s" % user.id, *sku_id_keys)
            else:
                redis_con.srem("selected_%s" % user.id, *sku_id_keys)

            return Response(serializer.data)
        else:
            cookie_str = request.COOKIES.get("cart")
            if cookie_str:
                cart_dict = str_to_dict(cookie_str)
            else:
                cart_dict = {}
            # 获取到字典中的每一项
            for sku_id in cart_dict.keys():
                cart_dict[sku_id]["selected"] = selected

            cookie_str = dict_to_str(cart_dict)
            resp = Response(serializer.data)
            resp.set_cookie("cart",cookie_str)
            return resp

